package com.blacksee.bluetoothpoker.ui;

import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
import com.blacksee.bluetoothpoker.logic.*;

public class PokerCanvas extends Canvas implements CommandListener {

	public static PokerCanvas instance;
	private static final int colorEventForeground = 0x000000;
	private static final int colorEventBackground = 0x7F7F7F;
	private static final int colorMenuSelectionBackground = 0x0000FF;
	private static final int colorMenuForeground = 0x000000;
	private static final int colorMessagePane = 0x3D0D51;

	private static final String SYMBOL_EURO = "\u20AC";
	private static final String SYMBOL_UP = "\u25B2";
	private static final String SYMBOL_DOWN = "\u25BC";
	private static final String ACTION_CALL = "Call";
	private static final String ACTION_FOLD = "Fold";
	private static final String ACTION_CHECK = "Check";
	private static final String ACTION_RAISE = "Raise";

	MIDlet app;
	
	private static final String PROJECT_NAME = "Bluetooth Poker";
	private static final String SKELETON_MES = "Skeleton";
	private static final String DEBUG_MES = "DEBUG";
	private static final String NET_MES = "NETWORKING";

	// colors
	private static final int colorGameBackground = 0x0cab00;
	private static final int colorGameHeadingBackground = 0x000000;
	private static final int colorGameHeadingForeground = 0xFFFFFF;	
	private static final int colorRed = 0x7F0000;
	private static final int colorBlack = 0x000000;

	// screens
	private static final String SYMBOL_SUIT_CLUBS = "\u2663";
	private static final String SYMBOL_SUIT_DIAMONDS = "\u2666";
	private static final String SYMBOL_SUIT_HEARTS = "\u2665";
	private static final String SYMBOL_SUIT_SPADES = "\u2660";
	
	private static final String CMD_OK = "Ok";
	private static final String CMD_BACK = "Back";
	private static final String CMD_START = "Start";
	private static final String CMD_JOIN = "Join";
	private static final String CMD_CREATE = "Create";
	private static final String CMD_LEAVE = "Leave";
	
	private Command CommandOk = new Command(CMD_OK, Command.OK, 1);
	private Command CommandStart = new Command(CMD_START, Command.OK, 1);
	private Command CommandJoin = new Command(CMD_JOIN, Command.OK, 1);
	private Command CommandCreate = new Command(CMD_CREATE, Command.OK, 1);
	private Command CommandBack = new Command(CMD_BACK, Command.BACK, 1);
	private Command CommandLeave = new Command(CMD_LEAVE, Command.STOP, 1);

	// strings
	private static final String[] CHOICE_MAIN_MENU = new String[]{"Join Table", "Create Table", "Exit"};
	private static final String HEADING_MAIN_MENU = "Welcome to Blue Poker";
	private static final String HEADING_CHOOSE_TABLE = "Choose a Table";
	private static final String HEADING_WAIT_FOR_JOIN = "Wait and Start";
	private static final String HEADING_WAIT_FOR_REPLY = "Wait for Reply...";
	private static final String HEADING_CREATE_TABLE = "Create Table";
	private static final String HEADING_ACCEPTED = "Accepted. Please wait...";
	private static final String HEADING_USER_NAME = "Set Name";
	
	private static final String[] actions1 = new String[]{ACTION_CHECK, ACTION_RAISE, ACTION_FOLD};
	private static final String[] actions2 = new String[]{ACTION_CALL, ACTION_RAISE, ACTION_FOLD};
	private static final int MENU_SHIFT = 4;
	
	private static final int SUBSTATE_IDLE = 0;
	private static final int SUBSTATE_TURN = 1;
	private static final int SUBSTATE_AMOUNT = 2;
	
	private boolean MDBG = false;
	
	private int substate = SUBSTATE_IDLE; 
	
	private int oldState = -1;
	private int oldPage = -1;
	
	private List waitForJoin;
	private List mainMenu;
	private List chooseTable;
	private Form waitForReply;
	private Form createTable;
	private TextField createTableText;
	private Form userName;
	private TextField userNameText;
	private Form accepted;
	private Display display;

	private TextBox debugScreen;

	private GameRunner game;
	private String[] cards;
	private Thread gameThread;
	private int selected;
	private int amount;
	Font font;

	private String[] getActions()
	{
		return game.getGameInfo().getPot() == 0 ? actions1 : actions2;
	}
	
	private void setCommands ()
	{
		switch (substate)
		{
		case SUBSTATE_IDLE:
			addCommand(CommandLeave);			
			break;
		case SUBSTATE_TURN:
			addCommand(CommandLeave);			
			addCommand(CommandOk);			
			break;
		case SUBSTATE_AMOUNT:
			addCommand(CommandBack);			
			addCommand(CommandOk);			
			break;
		}
	}
	
	private void resetCommands ()
	{
		switch (substate)
		{
		case SUBSTATE_IDLE:
			removeCommand(CommandLeave);			
			break;
		case SUBSTATE_TURN:
			removeCommand(CommandLeave);			
			removeCommand(CommandOk);			
			break;
		case SUBSTATE_AMOUNT:
			removeCommand(CommandBack);			
			removeCommand(CommandOk);			
			break;
		}
	}
	
	public PokerCanvas(MIDlet parentMidlet) {
		app = parentMidlet;
		instance = this;
		display = Display.getDisplay(app);

		mainMenu = new List(HEADING_MAIN_MENU, Choice.IMPLICIT, CHOICE_MAIN_MENU, null);
		mainMenu.setCommandListener(this);
		mainMenu.addCommand(CommandOk);

		chooseTable = new List(HEADING_CHOOSE_TABLE, Choice.IMPLICIT);
		chooseTable.setCommandListener(this);
		chooseTable.addCommand(CommandJoin);
		chooseTable.addCommand(CommandBack);

		waitForJoin = new List(HEADING_WAIT_FOR_JOIN, Choice.IMPLICIT);
		waitForJoin.setCommandListener(this);
		waitForJoin.addCommand(CommandStart);
		waitForJoin.addCommand(CommandBack);

		waitForReply = new Form(HEADING_WAIT_FOR_REPLY);
		waitForReply.setCommandListener(this);
		waitForReply.addCommand(CommandLeave);
		
		accepted = new Form(HEADING_ACCEPTED);
		accepted.setCommandListener(this);
		accepted.addCommand(CommandLeave);
		
		createTableText = new TextField("Enter name:", "NewTable", 30, TextField.ANY);
		createTable = new Form(HEADING_CREATE_TABLE, new Item[]{createTableText});
		createTable.setCommandListener(this);
		createTable.addCommand(CommandCreate);
		createTable.addCommand(CommandBack);

		userNameText = new TextField("Enter name:", "NewTable", 30, TextField.ANY);
		userName = new Form(HEADING_USER_NAME, new Item[]{userNameText});
		userName.setCommandListener(this);
		userName.addCommand(CommandOk);
		userName.addCommand(CommandBack);
		
		debugScreen = new TextBox("DEBUG:", "", 65536, TextField.ANY);

		String[] values = new String[]{"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
		cards = new String[53];		
		for (int i = 0; i < values.length; ++i)
		{
			cards[i] = values[i] + SYMBOL_SUIT_CLUBS;
			cards[i+13] = values[i] + SYMBOL_SUIT_DIAMONDS;
			cards[i+26] = values[i] + SYMBOL_SUIT_HEARTS;
			cards[i+39] = values[i] + SYMBOL_SUIT_SPADES;
		}
		cards[52] = "##";
		
		selected = 0;
		
		Font lf = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE);
		Font mf = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
		Font sf = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_SMALL);

		font = null;
		if (getWidth() / lf.charWidth('A') > 25)
			font = lf;
		else if (getWidth() / mf.charWidth('A') > 25)
			font = mf;
		else if (getWidth() / sf.charWidth('A') > 25)
			font = sf;
		else
			postDebugLn("We're sorry for inconvinience, but our game can't fit in the screen of your device.");	
		
		this.setCommandListener(this);
		game = new GameRunner(this);
		gameThread = new Thread(game);
	}

	private void drawString (Graphics g, String s, int x, int y)	{
		g.drawString(s, x, y, Graphics.BOTTOM | Graphics.LEFT);
	}
	
	public void postDebugLn (String str)
	{
		MDBG = true;
		debugScreen.setString(debugScreen.getString() + str + "\n");
		display.setCurrent(debugScreen);
	}

	private void drawStringCentered (Graphics g, String s, int y)	{
		g.drawString(s, (getWidth() - g.getFont().stringWidth(s)) / 2, y, Graphics.BOTTOM | Graphics.LEFT);
	}

	private void drawStringCenteredShadow (Graphics g, String s, int y, int colorShadow, int colorText)	{
		g.setColor(colorShadow);
		g.drawString(s, (getWidth() - g.getFont().stringWidth(s)) / 2+1, y, Graphics.BOTTOM | Graphics.LEFT);
		g.setColor(colorText);
		g.drawString(s, (getWidth() - g.getFont().stringWidth(s)) / 2, y, Graphics.BOTTOM | Graphics.LEFT);
	}

	private int cardsGetStringWidth (Graphics g, Card[] c)
	{
		int width = 0;
		
		for (int i = 0; i < c.length; ++i)
			width += g.getFont().stringWidth(cards[c[i].getIndex()]);
		width += g.getFont().stringWidth(",") * (c.length - 1);
		return width;
	}
	
	private void drawCards (Graphics g, int x0, int y, Card[] c)
	{
		int x = x0;
		for (int i = 0; i < c.length; ++i)
		{
			String s = cards[c[i].getIndex()];
			if (Card.isRed(c[i].getIndex()))
				g.setColor(colorRed);
			else
				g.setColor(colorBlack);
			drawString(g, s, x, y);
			x += g.getFont().stringWidth(s);
			if (i < c.length - 1)
			{
				g.setColor(colorBlack);
				drawString(g, ",", x, y);
				x += g.getFont().stringWidth(",");
			}
		}
	}
	
	protected void drawGameScreen (Graphics g) {
		// draw background
		g.setColor(colorGameBackground);
		g.fillRect(0, 0, getWidth(), getHeight());

		// draw heading background
		g.setColor(colorGameHeadingBackground);
		g.fillRect(0, 0, getWidth(), g.getFont().getHeight());

		// draw heading
		g.setColor(colorGameHeadingForeground);
		//drawStringCentered(g, heading, g.getFont().getHeight());
		
		//game.getGameInfo().getPlayers().elementAt(game.getGameInfo().)
		
		GameInfo gameInfo = game.getGameInfo();
		PlayerInfo playerInfo = game.getGameInfo().getUser();

		g.setFont(font);
				
		int fontHeight = g.getFont().getHeight();
		
		Card[] c = new Card[]{new Card(Card.SEVEN, Card.SPADES), new Card(Card.SIX, Card.CLUBS), new Card(Card.ACE, Card.DIAMONDS), new Card(Card.EIGHT, Card.HEARTS), new Card(Card.QUEEN, Card.SPADES)};
		drawCards(g, getWidth() - cardsGetStringWidth(g, c), fontHeight * 2, c);

		Card[] c2 = new Card[]{new Card(Card.KING, Card.SPADES), new Card(Card.ACE, Card.HEARTS)};
		drawCards(g, 0, fontHeight * 2, c2);
		
		int credit = playerInfo.getMoney();
		int total = gameInfo.getTotalMoneyOnTheTable();

		String stringCredit = SYMBOL_EURO + Integer.toString(credit);
		drawString(g, stringCredit, 0, fontHeight*3);
		
		String stringTotal = SYMBOL_EURO + Integer.toString(total);
		drawString(g, stringTotal, getWidth() - g.getFont().stringWidth(stringTotal), fontHeight*3);

		String[] actions = getActions();
		
		int maxActionWidth = 0;
		for (int i = 0; i < actions.length; ++i)
			maxActionWidth = Math.max(maxActionWidth, g.getFont().stringWidth(actions[i]));
		
		if (substate == SUBSTATE_TURN)
		{
			g.setColor(colorMenuSelectionBackground);
			g.fillRect(0, fontHeight * (MENU_SHIFT + selected - 1), getWidth(), fontHeight);			
			g.setColor(colorMenuForeground);
			for (int i = 0; i < actions.length; ++i)
				drawStringCentered(g, actions[i], fontHeight * (MENU_SHIFT + i));
		} else if (substate == SUBSTATE_AMOUNT) {
			g.setColor(colorMenuForeground);
			drawStringCentered(g, "Amount: " + amount, fontHeight * (MENU_SHIFT + 3));
			drawStringCentered(g, "        " + SYMBOL_UP, fontHeight * (MENU_SHIFT + 2));
			drawStringCentered(g, "        " + SYMBOL_DOWN, fontHeight * (MENU_SHIFT + 4));
		}
 		
 		int totalHeight = (MENU_SHIFT + 4) * fontHeight;
 		g.setColor(colorMessagePane);
 		g.fillRect(0, totalHeight, getWidth(), getHeight() - totalHeight);
 		
 		Vector events = new Vector();//game.getGameInfo().getEvents();
 		events.addElement("Welcome to Blue Poker");
 		events.addElement("Game start");
 		events.addElement("Bob checks");
 		
 		int margin = 2;
 		for (int i = 0; i < events.size() && totalHeight + fontHeight * (i + 1) + margin * (i + 2) < getHeight(); ++i)
 		{
 			g.setColor(colorEventBackground);
 			g.fillRect(margin, totalHeight + fontHeight * i + margin * (i + 1), getWidth() - 2 * margin, fontHeight);
 			g.setColor(colorEventForeground);
 			drawString(g, (String)events.elementAt(events.size() - i - 1), margin, totalHeight + fontHeight * (i + 1) + margin * (i + 1));
 		}
    }

	private boolean isMyTurn() {
		return game.getGameInfo().getPlayerInTurn() == game.getGameInfo().getUser().getOrderNumber();
	}

	protected void drawInitialScreen (Graphics g) {
		g.setColor(0xFFFFFF);
		g.fillRect(0, 0, getWidth(), getHeight());
		drawStringCenteredShadow(g, PROJECT_NAME, (getHeight() - g.getFont().getHeight()) / 2 - g.getFont().getHeight(), 0x707070, 0x000000);
		g.setColor(0x000000);
		drawStringCentered(g, SKELETON_MES, (getHeight() - g.getFont().getHeight()) / 2 + g.getFont().getHeight());
		drawStringCentered(g, DEBUG_MES, (getHeight() - g.getFont().getHeight())/ 2 + g.getFont().getHeight() * 2);
		drawStringCentered(g, NET_MES, (getHeight() - g.getFont().getHeight()) / 2 + g.getFont().getHeight() * 3);
	}

	protected void paint(Graphics g) {		
		if (game.getState() == GameRunner.MENU_STATE) {
			//switchScreen();
		} else {
			if (substate == SUBSTATE_IDLE && isMyTurn())
			{
				resetCommands();
				substate = SUBSTATE_TURN;
				setCommands();
			}
			else if (substate == SUBSTATE_TURN && !isMyTurn())
			{
				resetCommands();
				substate = SUBSTATE_IDLE;
				setCommands();
			}
				
			drawGameScreen(g);			
		}
	}

	private void updateScreen () {
		switch (game.getMenuPage())
		{
		case GameRunner.MAIN_MENU_PAGE:
			break;
		case GameRunner.WAITING_PLAYERS_PAGE:
			waitForJoin.deleteAll();
			Hashtable players = game.getMenuInfo().getPlayersList();
			Enumeration playersNames = players.elements() ;
			while(playersNames.hasMoreElements()){
				waitForJoin.append((String)playersNames.nextElement(), null);				
			}
			display.setCurrent(waitForJoin);
			break;
		case GameRunner.WAITING_ACCEPTANCE_PAGE:
			break;
		case GameRunner.JOIN_GAME_PAGE:
			chooseTable.deleteAll();
			Vector tables = game.getMenuInfo().getTablesList();
			for (int i = 0; i < tables.size(); ++i)
				chooseTable.append((String)tables.elementAt(i),null);
			display.setCurrent(chooseTable);
			break;
		case GameRunner.CREATE_GAME_PAGE:			
			break;
		case GameRunner.CONFIRMATION_PAGE:
			break;
		}
	}
	
	private void switchScreen() {
		if (game.getState() == GameRunner.MENU_STATE) {
			switch (game.getMenuPage())
			{
			case GameRunner.MAIN_MENU_PAGE:
				display.setCurrent(mainMenu);
				break;
			case GameRunner.WAITING_PLAYERS_PAGE:
				display.setCurrent(waitForJoin);
				break;
			case GameRunner.WAITING_ACCEPTANCE_PAGE:
				display.setCurrent(waitForReply);			
				break;
			case GameRunner.JOIN_GAME_PAGE:
				display.setCurrent(chooseTable);
				break;
			case GameRunner.CREATE_GAME_PAGE:			
				display.setCurrent(createTable);
				break;
			case GameRunner.CONFIRMATION_PAGE:
				display.setCurrent(accepted);
				break;
			}
		} else {
			System.out.println("Switch to game");
			display.setCurrent(this);
			//postDebugLn("#" + game.getGameInfo().getUser().getOrderNumber());
		}
	}
	
	protected void keyPressed(int keyCode) {
		if (game.getState() == GameRunner.MENU_STATE)
		{
			super.keyPressed(keyCode);
			return;
		}		
		
        int gameAction = getGameAction(keyCode);
        if (game.getState() == GameRunner.MENU_STATE) {
			switch (game.getMenuPage())
			{
			case GameRunner.MAIN_MENU_PAGE:
		        if (gameAction == FIRE)
		        	pushMainMenuOk();				
				break;
			case GameRunner.WAITING_PLAYERS_PAGE:
		        if (gameAction == FIRE)
		        	pushStart();
				break;
			case GameRunner.WAITING_ACCEPTANCE_PAGE:
		        if (gameAction == FIRE)
		        	pushLeave();				
				break;
			case GameRunner.JOIN_GAME_PAGE:	
		        if (gameAction == FIRE)
		        	pushJoin();				
				break;
			case GameRunner.CREATE_GAME_PAGE:			
		        if (gameAction == FIRE)
		        	pushCreate();				
				break;
			case GameRunner.CONFIRMATION_PAGE:
		        if (gameAction == FIRE)
		        	pushLeave();				
				break;
			}
		} else {
			if (substate == SUBSTATE_AMOUNT) {
		        if (gameAction == UP && amount < game.getGameInfo().getUser().getMoney() - 1)
		        	amount++;
		        else if (gameAction == DOWN && amount > game.getGameInfo().getPot())
		        	amount--;
		        else if (gameAction == FIRE)
		        	pushAction();
			} else {
		        if (gameAction == UP && selected > 0)
		        	selected--;
		        else if (gameAction == DOWN && selected < getActions().length - 1)
		        	selected++;
		        else if (gameAction == FIRE)
		        	pushAction();
			}
	        repaint();
		}
	}
	
	private void pushMainMenuOk ()
	{	
		display.setCurrent(null);
		int s = mainMenu.getSelectedIndex();
		//postDebugLn("client scenario");
		if (s == 0) {			
			game.selectJoinGameOption();							
		} else if (s == 1) {
			game.selectCreateGameOption();
		} else {			
			app.notifyDestroyed();
		}		
	}
	
	public void forceRepaint()
	{
		if (MDBG)
			return;
		if (game.getState() != oldState || game.getMenuPage() != oldPage) {			
			switchScreen();
			oldState = game.getState();
			oldPage = game.getMenuPage();
		}
		if (game.getState() == GameRunner.GAME_STATE) {
			repaint();
		} else {			
			updateScreen();
		}
	}
	
	private void pushStart()
	{
		game.startGame();
	}

	private void pushBack()
	{
		game.backToMainMenu();
	}

	private void pushJoin()
	{
		game.joinGame(chooseTable.getSelectedIndex());
	}

	private void pushCreate() 
	{
		String name = createTableText.getString();
		if (name .length() > 0)
			game.createGame(name);
	}

	private void pushLeave() 
	{
		game.backToMainMenu();		
	}

	private void pushAction() 
	{
		if (substate == SUBSTATE_AMOUNT) {
			game.raise(amount);			
			resetCommands();
			substate = SUBSTATE_IDLE;
			setCommands();
		} else {
			String action = getActions()[selected];
			if (action == ACTION_CHECK) {
				game.check();
			} else if (action == ACTION_CALL) {
				game.call();
			} else if (action == ACTION_RAISE) {
				resetCommands();
				substate = SUBSTATE_AMOUNT;
				setCommands();
				amount = game.getGameInfo().getPot();
				repaint();
			} else if (action == ACTION_FOLD) {
				game.fold();
			}
			selected = 0;
		}
	}

	private void pushGameBack() 
	{
		resetCommands();
		 substate = SUBSTATE_TURN;
		setCommands();
	}

	public void commandAction(Command cmd, Displayable obj) {
		System.out.println("Command action");
		if (cmd.getLabel().length() < 1)
			return;
		if (game.getState() == GameRunner.MENU_STATE) {
			switch (game.getMenuPage())
			{
			case GameRunner.MAIN_MENU_PAGE:
				if (cmd.getLabel() == CMD_OK) {
					pushMainMenuOk();
				} 
				break;
			case GameRunner.WAITING_PLAYERS_PAGE:
				if (cmd.getLabel() == CMD_START) {
					pushStart();
				} else if (cmd.getLabel() == CMD_BACK) {
					pushBack();
				}
				break;
			case GameRunner.WAITING_ACCEPTANCE_PAGE:
				if (cmd.getLabel() == CMD_LEAVE) {
					pushLeave();
				}
				break;
			case GameRunner.JOIN_GAME_PAGE:	
				if (cmd.getLabel() == CMD_JOIN) {
					pushJoin();
				} else if (cmd.getLabel() == CMD_BACK) {
					pushBack();
				}
				break;
			case GameRunner.CREATE_GAME_PAGE:			
				if (cmd.getLabel() == CMD_CREATE) {
					pushCreate();
				} else if (cmd.getLabel() == CMD_BACK) {
					pushBack();
				}
				break;
			case GameRunner.CONFIRMATION_PAGE:
				if (cmd.getLabel() == CMD_LEAVE) {
					pushLeave();
				}
				break;
			}
		} else {
			if (cmd.getLabel() == CMD_OK) {
				pushAction();
			} else if (cmd.getLabel() == CMD_LEAVE) {
				pushLeave();
			} else if (cmd.getLabel() == CMD_BACK) {
				pushGameBack();
			}
		}
	}	
	
	public void runGame() {		
		gameThread.start();		
	}
}
